using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Xml;
using System.Windows.Controls;
using MapSurface.WidgetsProperties;
using MapSurface.UIModelElement;
using System.Windows.Markup;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;


namespace MapSurface.UIModelElement
{
    public class AssemblyReflexion
    {
        #region Properties
        private Assembly _widgets = null;

        public Assembly Widgets
        {
            get { return _widgets; }
            set { _widgets = value; }
        }
        private Assembly _standard = null;

        public Assembly Standard
        {
            get { return _standard; }
            set { _standard = value; }
        }
        private Assembly _presentation = null;

        public Assembly Presentation
        {
            get { return _presentation; }
            set { _presentation = value; }
        }
        private List<Delegate> _selectEventHandler = null;

        public List<Delegate> SelectEventHandler
        {
            get {
                if (_selectEventHandler == null)
                    _selectEventHandler = new List<Delegate>(); 
                return _selectEventHandler; }
            set { _selectEventHandler = value; }
        }
        private Object _context = null;

        public Object Context
        {
            get { return _context; }
            set { _context = value; }
        }

        private List<RoutedEvent> _routedEvent = null;

        public List<RoutedEvent> RoutedEvent
        {
            get {
                if (_routedEvent == null)
                    _routedEvent = new List<RoutedEvent>();
                return _routedEvent;
            }
            set { _routedEvent = value; }
        }
        #endregion
        #region Instances
        public AssemblyReflexion(string dllDir,String contextFile)
        {

            _widgets = System.Reflection.Assembly.LoadFrom(dllDir + @"\Microsoft.Surface.Presentation.dll");
            _standard = System.Reflection.Assembly.LoadFrom(dllDir + @"\PresentationFramework.dll");
        }
        private void getInstanceElement(ref string widgetName, ref Object element)
        {
            if (_widgets != null)
            {
                try
                {
                    if (widgetName.StartsWith("s:"))
                    {
                        string name = widgetName.Substring(2);
                        widgetName = name;
                    }
                    Type type = _widgets.GetType("Microsoft.Surface.Presentation.Controls." + widgetName);
                    if (type != null)
                        element = Activator.CreateInstance(type);
                    else
                    {
                        type = _standard.GetType("System.Windows.Controls." + widgetName);
                        if (type == null)
                            type = _standard.GetType("System.Windows.Shapes." + widgetName);
                        if (type != null)
                            element = Activator.CreateInstance(type);
 
                    }
                    if(element!=null)
                    for (int i = 0; i < _routedEvent.Count; i++)
                    {
                        try
                        {
                            ((System.Windows.UIElement)element).AddHandler(_routedEvent[i], _selectEventHandler[i]);
                        }
                        catch (Exception)
                        {
                            ((FrameworkElement)element).AddHandler(_routedEvent[i], _selectEventHandler[i]);
                        }

#if DEBUG
                        Console.Out.WriteLine("[DEBUG]: AddHandler {0} {1} {2}", element.GetType().ToString(), _routedEvent[i], _selectEventHandler[i]);
#endif
                    }



                }
                catch (Exception e)
                {
#if DEBUG
                    Console.WriteLine("[DEBUG]: getInstanceElement " + e.Message);
#endif
                }
            }
        }    
        public Object getInstance(string widgetName,   MapSurface.UIModelElement.UIElement current)
        {
            Object element = null;
            getInstanceElement(ref widgetName, ref element);
            if (element != null)
            {
                element = setAttributes(element, current);
                element = setEvents(element, current, _routedEvent, SelectEventHandler,_context);
                element = setEventsSelect(element, current, _context);
            }
            return element;
        }

        #endregion
        #region Handler Setters
        private Object setEventsSelect(Object element, UIModelElement.UIElement current,Object context)
        {
            try
            {
                //ScatterManipulationDelta="handler"
                setHandler(element, "Click", context, "handler");
                setHandler(element, "ContactDown", context, "handler");
                setHandler(element, "SizeChanged", context, "handler");
                setHandler(element, "ScatterManipulationDelta", context, "handler");
            }
            catch (Exception e)
            {
                Console.WriteLine(e);

            }
            return element;

        }
        public void setHandler( Object instance, String evenName, Object context, String handler)
        {
            try
            {
                Type t = instance.GetType();
                System.Reflection.MethodInfo mi = context.GetType().GetMethod(handler);
                Type t2 = t.GetEvent(evenName).EventHandlerType;
                if (t2 != null)
                {
                    var d = EventHandler.CreateDelegate(t2, context, mi);
                    t.GetEvent(evenName).AddEventHandler(instance, d);
                }

            }catch(Exception e)
            {
#if DEBUG
                Console.WriteLine("[DEBUG] setHandler " + e.Message);   
#endif
            }
        }
        #endregion
        #region Attributs Setters
        public Object setAttributes(Object element, UIModelElement.UIElement node)
        {
            ParseValue p = new ParseValue();
            Type type = element.GetType();
            PropertyInfo pi;
            if (node.Properties.Count != 0)
            {
                foreach (UIModelElement.Property att in node.Properties)
                {
                    try
                    {
                        pi = type.GetProperty(att.Name);
                        if (pi != null)
                        {
                            MethodInfo mi = pi.GetSetMethod(true);
                            object[] param = null;
                            if (p.IsDouble(att.Value))
                                param = new object[] { p.parseStrToDouble(att.Value) };
                            else if (p.isMargin(att.Value))
                                param = new object[] { p.parseToMargin(att.Value) };
                            else if (p.isBoolean(att.Value))
                                param = new object[] { p.parseToBoolean(att.Value) };
                            else if (att.Name.CompareTo("Visibility") == 0)
                                param = new object[] { p.parseToVisibility(att.Value) };
                            else if (p.isBrush(att.Value))
                                param = new object[] { p.parseToBrush(att.Value) };
                            else
                                param = new object[] { att.Value.ToString() };
                            if (mi != null)
                                mi.Invoke(element, param);
                        }
                    }
                    catch (Exception e)
                    {
#if DEBUG
                        Console.WriteLine("[DEBUG] setAttributes " + e.StackTrace);
#endif

                    }
                }
            }
            pi=null;
            if(isContentControl(element) && !element.GetType().Name.Contains("Scatter"))
                pi = type.GetProperty("Content");
            PropertyInfo pname = type.GetProperty("Name");
            if (pi == null)
                pi = type.GetProperty("Text");
            if (pi != null )
            {

                MethodInfo mi = pi.GetSetMethod(true);
                object[] param = new object[] { node.Name};
                if (string.Compare((String)param[0], "") != 0)
                    mi.Invoke(element, param);
            }
            if (pname != null)
            {

                MethodInfo mi = pname.GetSetMethod(true);
                object[] param = new object[] { node.Name };
                if (string.Compare((String)param[0], "") != 0)
                    mi.Invoke(element, param);
            }

            //Console.Out.WriteLine(XamlWriter.Save(element));
            element = setDefaultValue(element);
            return element;
        }
        public Object setAttributes(Object element, UIModelElement.Container node)
        {
            ParseValue p = new ParseValue();
            Type type = element.GetType();
            PropertyInfo pi;
            if (node.Properties.Count != 0)
            {
                foreach (UIModelElement.Property att in node.Properties)
                {
                    try
                    {
                        pi = type.GetProperty(att.Name);
                        if (pi != null)
                        {
                            MethodInfo mi = pi.GetSetMethod(true);
                            object[] param = null;
                            if (p.IsDouble(att.Value))
                                param = new object[] { p.parseStrToDouble(att.Value) };
                            else if (p.isMargin(att.Value))
                                param = new object[] { p.parseToMargin(att.Value) };
                            else if (p.isBoolean(att.Value))
                                param = new object[] { p.parseToBoolean(att.Value) };
                            else if (att.Name.CompareTo("Visibility") == 0)
                                param = new object[] { p.parseToVisibility(att.Value) };
                            else if (p.isBrush(att.Value))
                                param = new object[] { p.parseToBrush(att.Value) };
                            else
                                param = new object[] { att.Value.ToString() };
                            if (mi != null)
                                mi.Invoke(element, param);
                        }
                    }
                    catch (Exception e)
                    {
#if DEBUG
                        Console.WriteLine("[DEBUG] setAttributes " + e.StackTrace);
#endif
                    }
                }

            }
           // pi = type.GetProperty("Content");
            PropertyInfo pname = type.GetProperty("Name");
           // if (pi == null)
            pi = type.GetProperty("Text");
            if (pi != null )
            {

                MethodInfo mi = pi.GetSetMethod(true);
                object[] param = new object[] { node.Name};
                if (string.Compare((String)param[0], "") != 0)
                    mi.Invoke(element, param);
            }
            if (pname != null)
            {

                MethodInfo mi = pname.GetSetMethod(true);
                object[] param = new object[] { node.Name };
                if (string.Compare((String)param[0], "") != 0)
                    mi.Invoke(element, param);
            }

            Console.Out.WriteLine(XamlWriter.Save(element));
            return element;
        }
        public String getAttribute(Object element,string attibuteName)
        {
            String value = "";
            try
            {
                object o = null;
                Type type = element.GetType();
                PropertyInfo pi = type.GetProperty(attibuteName);
                if (pi != null)
                 {
                        MethodInfo [] mi = pi.GetAccessors();
                        object[] param = null;
                        if (mi != null)
                            o = pi.GetValue(element, param);
                                //mi[0].Invoke(element, param);
                        if (o != null)
                            value = o.ToString();

                    }
                }
            catch (Exception)
            {
                //;
            }
            return value;
        }
        public void setAttribute(ref Object element, string attibuteName, object value)
        {
            try
            {
                Type type = element.GetType();
                PropertyInfo pi = type.GetProperty(attibuteName);
                if (pi != null)
                {
                    object[] param = {value};
                    MethodInfo mi = pi.GetSetMethod();
                    //pi.SetValue(element, value, param);
                    if(mi!=null)
                        mi.Invoke(element, param);


                }
            }
            catch (Exception )
            {
                //;
            }
        }

        public Type getType(String widgetName)
        {
            return _widgets.GetType("Microsoft.Surface.Presentation.Controls." + widgetName);
        }
        public void displayProperties(Object container, Object uiElement)
        {
            PropertyInfo[] pi = uiElement.GetType().GetProperties();
            foreach(PropertyInfo i in pi)
            {
                Object value=i.GetValue(uiElement,null);

                if (value != null  && value.ToString().CompareTo("False")==0)
                {
                    ListBoxItem lbi = new ListBoxItem();
                    lbi.Content = i.Name + "  " + value.ToString();
                    ((ListBox)container).Items.Add(lbi);
                }
            }
        }       
        private Object setProperty(Type type,String name, Object value, Object instance)
        {
            PropertyInfo pi = type.GetProperty(name);
            if (pi != null)
                pi.SetValue(instance,value, null);
            #if DEBUG
                Console.WriteLine("[DEBUG] setproperty name={0} value={1} at {2} object ",name,value.ToString(),instance.ToString());
            #endif
            return instance;
        }
        public Object addChild(Object objectUI, Object obj)
        {
            if (obj != null)
                if (objectUI != null)
                {
                    if (isItemControl(objectUI))
                        ((ItemsControl)objectUI).Items.Add(obj);
                    else if (isPanel(objectUI))
                        ((Panel)objectUI).Children.Add((System.Windows.UIElement)obj);
                    else if (isContentControl(objectUI))
                        ((ContentControl)objectUI).Content = obj;
                    else if (hasChild(objectUI))
                        setVisualChild(objectUI, obj);
                }
                else
                    objectUI = obj;
            return objectUI;
        }
        private Object setEvents(Object element, UIModelElement.UIElement current, List<RoutedEvent> _routedEvent, List<Delegate> _selectEventHandler, Object context)
        {
            if (current.Events != null)
                foreach (Event e in current.Events)
                {
                    try
                    {
                        setHandler(element, e.Name, context, "handler");

                    }
                    catch (Exception)
                    {

                    }
                }

            return element;

        }

        #endregion
        #region finder methods

        public Border setBorder(System.Windows.UIElement neested)
        {
            Border border = new Border();
            border.BorderBrush = Brushes.Black;
            border.BorderThickness = new Thickness(3);
            border.Name = "SelectBorder";
            Object s= getAttribute(neested,"RenderSize");
            if (s != null)
            {
                string[] size = s.ToString().Split(';');
                border.Width=Double.Parse( size[0])+3;
               border.Height = Double.Parse( size[1])+3;
            }
            if(border.Child==null)
                border.Child=neested;
            border.Background = Brushes.Blue;
            return border;
        }

        
        public int selectUIElement(ref Object objectUI, Object element)
        {
            String name= getAttribute(objectUI,"Name");
            if (name != null && name.CompareTo("SelectBorder")==0)
            {
                objectUI = ((Border)objectUI).Child;
            }
            if (objectUI != null && (objectUI.GetHashCode() == element.GetHashCode()))
                return 0;
            else
            {
                if (hasChild(element))
                {
                    List<Object> childs = getChild(objectUI);
                    foreach (Object o in childs)
                    {
                        Object it = o;
                        int ret = selectUIElement(ref it, element);
                        if (ret == 0)
                        {
                            objectUI= removeVisualChid(objectUI, o);
                            Border b = setBorder((System.Windows.UIElement)o);
                            setVisualChild(objectUI, b);
                            return 1;
                        }
                        else if (ret == 1)
                            return 1;
                    }
                }
            }
            return -1;
        }

        /// <summary>
        /// This Method find and instance of a given object into UI Model
        /// </summary>
        /// <param name="objectUI"></param>
        /// <param name="element"></param>
        /// <returns></returns>
        public bool contains(Object objectUI, Object element)
        {
            if (objectUI != null && (objectUI.GetHashCode() == element.GetHashCode()))
                return true;
            else
            {
                if (hasChild(element))
                {
                    List<Object> childs = getChild(objectUI);
                    foreach (Object o in childs)
                    {
                        if (contains(o, element))
                            return true;
                    }

                }
                else if (isItemControl(objectUI))
                {
                    ItemsControl ic = ((ItemsControl)objectUI);
                    for  (int index=0;index<ic.Items.Count;index++)
                    {
                        if (contains(ic.Items[index], element))
                            return true;
                    }
                }
                else if (isPanel(objectUI))
                {
                    Panel p = (Panel)objectUI;
                    foreach (System.Windows.UIElement pi in p.Children)
                    {
                        if (contains(pi, element))
                            return true;
                    }
                }
                else if (isContentControl(objectUI))
                {
                    ContentControl cc = (ContentControl)objectUI;
                    if (contains(cc.Content, element))
                        return true;
                }
            }
            return false;
        }
        public Boolean isItemControl(Object element)
        {
            try
            {

                ItemsControl obj = (ItemsControl)element;
                return true;
            }
            catch (InvalidCastException e)
            {
#if DEBUG
                Console.Out.WriteLine("[DEBUG] isItemControl " + e.Message);
#endif


            }
            return false;
        }
        public Boolean isPanel(Object element)
        {
            try
            {
                Panel p = (Panel)element;
                return true;
            }
            catch (InvalidCastException e)
            {
#if DEBUG
                Console.Out.WriteLine("[DEBUG] isPanel " + e.Message);
#endif


            }
            return false;
        }
        public Boolean isContentControl(Object element)
        {
            try
            {
                ContentControl c = (ContentControl)element;
                return true;
            }
            catch (InvalidCastException e)
            {
#if DEBUG
                Console.Out.WriteLine("[DEBUG] isContentControl " + e.Message);
#endif
            }
            return false;
        }
        public Boolean hasChild(Object element)
        {
            try
            {
                Type t = element.GetType();
                System.Reflection.MethodInfo mi = t.GetMethod ("GetVisualChild",BindingFlags.Instance | BindingFlags.NonPublic);
                if(mi!=null)
                    return true;
            }
            catch (Exception e)
            {
#if DEBUG
                Console.Out.WriteLine("[DEBUG] hasChild " + e.Message);
#endif

            }
            return false;

        }
        public List<Object> getChild(Object element)
        {
            List<Object> childs = new List<object>();
            if (hasChild(element))
            {
                Type t = element.GetType();
                System.Reflection.MethodInfo mi = t.GetMethod("GetVisualChild",BindingFlags.Instance | BindingFlags.NonPublic);
                System.Reflection.MethodInfo min = t.GetMethod("VisualChildrenCount", BindingFlags.Instance | BindingFlags.NonPublic);
                Object rt = null;
                Object[] param = null;
                int i = 0;
                if (mi != null )
                {
                    int nchild = 10;
                    if(min!=null)
                        nchild=(int)min.Invoke(element, param);
                    while (i > -1 && i < nchild)
                    {
                        try
                        {
                            param = new object[] { i };
                            rt = mi.Invoke(element, param);
                            if (rt != null)
                            {
                                childs.Add(rt);
                                i++;
                            }
                            else
                                i = -1;
                        }
                        catch (Exception)
                        {
                            i = -1;

                        }
                    }
                }
            }
            return childs;
        }
        public void setVisualChild(Object element, Object child)
        {
            if (hasChild(element))
            {
                try
                {
                    setProperty(element.GetType(), "Child", child, element);
                    
                }
                catch (Exception e)
                {
                    Type t = element.GetType();
                    System.Reflection.MethodInfo mi = t.GetMethod("AddVisualChild", BindingFlags.Instance | BindingFlags.NonPublic);
                    Object[] param = { child };
                    if (mi != null)
                        mi.Invoke(element, param);
#if DEBUG
                    Console.Out.WriteLine("[DEBUG] setVisualChild " + e.Message);
#endif
                }
            }

        }
        public Object removeVisualChid(Object element, Object child)
        {
            if (hasChild(element))
            {
                try
                {
                    Type t = element.GetType();
                    System.Reflection.MethodInfo mi = t.GetMethod("RemoveLogicalChild", BindingFlags.Instance | BindingFlags.NonPublic);
                    Object[] param = { child };
                    if (mi != null)
                        mi.Invoke(element, param);
                    mi = t.GetMethod("RemoveVisualChild", BindingFlags.Instance | BindingFlags.NonPublic);
                    if(mi!=null)
                        mi.Invoke(element, param);
                }
                catch (Exception e)
                {
#if DEBUG
                    Console.Out.WriteLine("[DEBUG] removeChild " + e.Message);
#endif
                }
            }
            return element;

        }
        public Object removeChild(Object element, ref Object child)
        {
            if (isItemControl(element))
            {
                ((ItemsControl)element).Items.Remove(child);
            }
            else if (isPanel(element))
            {
                try
                {
                    ((Panel)element).Children.Remove((System.Windows.UIElement)child);
                    //((ItemCollection)element).Remove((System.Windows.UIElement)child);
                    //   removeVisualChid(element, child);
                }
                catch (Exception)
                {
                    //((FrameworkElement)element).Get
                }
            }//TODO: Remove from visual child
            else
            {
                child = null;
            }
            return element;
        }
        public Boolean isImage(Object element)
        {
            if (element != null)
                return element.GetType().Name.Contains("Image");
            return false;
        }
        public Object setDefaultValue(Object element)
        {
            if(isImage(element))
            {
                Type type=element.GetType();
                BitmapImage bi3 = new BitmapImage();
                bi3.BeginInit();
                bi3.UriSource = new Uri(".\\Resources\\icon.PNG", UriKind.Relative);
                bi3.EndInit();
                setProperty(type, "Source", bi3, element);
            }
            FlowDirection f= FlowDirection.LeftToRight;
            setAttribute(ref element,"FlowDirection",f);
            return element;
        }
        #endregion
 
    }
}
